/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * ident	"%Z%%M%	%I%	%E% SMI"
 */
package org.opensolaris.os.dtrace;

import java.io.*;
import java.beans.*;

/**
 * A value generated by the DTrace {@code mod()}, {@code func()}, or
 * {@code sym()} action used to lookup the symbol associated with a
 * kernel address.
 * <p>
 * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
 *
 * @author Tom Erickson
 */
public final class KernelSymbolRecord implements SymbolValueRecord,
       Serializable, Comparable <KernelSymbolRecord>
{
    static final long serialVersionUID = -7156627773519296848L;

    static {
	try {
	    BeanInfo info = Introspector.getBeanInfo(KernelSymbolRecord.class);
	    PersistenceDelegate persistenceDelegate =
		    new DefaultPersistenceDelegate(
		    new String[] {"symbol", "address"})
	    {
		/*
		 * Need to prevent DefaultPersistenceDelegate from using
		 * overridden equals() method, resulting in a
		 * StackOverFlowError.  Revert to PersistenceDelegate
		 * implementation.  See
		 * http://forum.java.sun.com/thread.jspa?threadID=
		 * 477019&tstart=135
		 */
		protected boolean
		mutatesTo(Object oldInstance, Object newInstance)
		{
		    return (newInstance != null && oldInstance != null &&
			    oldInstance.getClass() == newInstance.getClass());
		}
	    };
	    BeanDescriptor d = info.getBeanDescriptor();
	    d.setValue("persistenceDelegate", persistenceDelegate);
	} catch (IntrospectionException e) {
	    e.printStackTrace();
	}
    }

    /** @serial */
    private String symbol; // set natively after creation; treat as final
    /** @serial */
    private final long address;

    /**
     * Called by native code.
     */
    private
    KernelSymbolRecord(long addressValue)
    {
	address = addressValue;
    }

    /**
     * Creates a {@code KernelSymbolRecord} with the given symbol lookup
     * and kernel address converted in probe context as a result of the
     * DTrace {@code mod()}, {@code func()}, or {@code sym()} action.
     * <p>
     * Supports XML persistence.
     *
     * @param addressValue symbol address
     * @param lookupValue the result in the native DTrace library of
     * looking up the symbol associated with the given kernel address
     * @throws NullPointerException if the given lookup value is {@code null}
     */
    public
    KernelSymbolRecord(String lookupValue, long addressValue)
    {
	symbol = lookupValue;
	address = addressValue;
	validate();
    }

    private final void
    validate()
    {
	if (symbol == null) {
	    throw new NullPointerException("symbol is null");
	}
    }

    /**
     * Gets the result of the address lookup in the same form returned
     * by {@link Consumer#lookupKernelFunction(long address)}.
     *
     * @return non-null address lookup in the format defined by the
     * native DTrace library
     */
    public String
    getSymbol()
    {
	return symbol;
    }

    /**
     * Called by native code and ProbeData addSymbolRecord()
     */
    void
    setSymbol(String lookupValue)
    {
	symbol = lookupValue;
	validate();
    }

    /**
     * Gets the symbol's kernel address.
     *
     * @return the symbol's kernel address
     */
    public long
    getAddress()
    {
	return address;
    }

    /**
     * Gets the symbol's kernel address.  The value is used in {@link
     * #equals(Object o) equals()} and {@link
     * #compareTo(KernelSymbolRecord r) compareTo()} to test equality
     * and to determine the natural ordering of {@code
     * KernelSymbolRecord} instances.
     *
     * @return non-null value of the symbol's kernel address
     */
    public Long
    getValue()
    {
	return address;
    }

    /**
     * Compares the specified object with this {@code KernelSymbolRecord}
     * for equality.  Returns {@code true} if and only if the specified
     * object is also a {@code KernelSymbolRecord} and both records have
     * the same address.
     *
     * @return {@code true} if and only if the specified object is also
     * a {@code KernelSymbolRecord} and both records have the same
     * address
     */
    @Override
    public boolean
    equals(Object o)
    {
	if (o instanceof KernelSymbolRecord) {
	    KernelSymbolRecord r = (KernelSymbolRecord)o;
	    return (address == r.address);
	}
	return false;
    }

    /**
     * Overridden to ensure that equal instances have equal hash codes.
     */
    @Override
    public int
    hashCode()
    {
	return (int)(address ^ (address >>> 32));
    }

    /**
     * Compares this record with the given kernel symbol lookup and
     * orders by address.  The comparison treats addresses as unsigned
     * values so the ordering is consistent with that defined in the
     * native DTrace library. The {@code compareTo()} method is
     * compatible with {@link #equals(Object o) equals()}.
     *
     * @return -1, 0, or 1 as this record's address is less than, equal
     * to, or greater than the given record's address
     */
    public int
    compareTo(KernelSymbolRecord r)
    {
	return ProbeData.compareUnsigned(address, r.address);
    }

    private void
    readObject(ObjectInputStream s)
            throws IOException, ClassNotFoundException
    {
	s.defaultReadObject();
	// check class invariants
	try {
	    validate();
	} catch (Exception e) {
	    InvalidObjectException x = new InvalidObjectException(
		    e.getMessage());
	    x.initCause(e);
	    throw x;
	}
    }

    /**
     * Gets the result of this symbol lookup.  The format is defined in
     * the native DTrace library and is as stable as that library
     * definition.
     *
     * @return {@link #getSymbol()}
     */
    @Override
    public String
    toString()
    {
	return symbol;
    }
}
